﻿using AutoMapper;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using Prism.Events;
using Prism.Ioc;
using FunasrSTT.Common.Helper;
using FunasrSTT.Translate.Services;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using FunasrSTT.Translate.Models.FunASR;
using System.Collections.Concurrent;
using Websocket.Client;
using Microsoft.Win32;
using System.Text.Json;

namespace FunasrSTT.Translate.ViewModels;

public partial class SpeechToTextViewModel : ViewModelBase
{
    /// <summary>
    /// 服务器地址
    /// </summary>
    [ObservableProperty]
    private string _serverIp = "127.0.0.1";

    [ObservableProperty]
    private int _serverPort = 10095;

    /// <summary>
    /// 语音识别类型，2pass,online,offline
    /// </summary>
    [ObservableProperty]
    private ASRModeType _aSRModeType = ASRModeType.TwoPass;

    /// <summary>
    /// 语音输入模式，麦克风或语音文件
    /// </summary>
    [ObservableProperty]
    private RecoderModeType _recoderModeType = RecoderModeType.Mic;

    [ObservableProperty]
    private string _asrMessageResult = string.Empty;

    [ObservableProperty]
    private string _wavFilePath = string.Empty;

    #region 按钮控制
    [ObservableProperty]
    private bool _isEnableWavFile = false;
    [ObservableProperty]
    private bool _isEnableConnect = true;
    [ObservableProperty]
    private bool _isEnableDisConnect = false;
    [ObservableProperty]
    private bool _isEnableStart = false;
    [ObservableProperty]
    private bool _isEnableStop = false;
    #endregion

    private static WebSocketService websocketClient; //实例化websocket类
    private static WaveCollectService waveCollect;//实例化语音采集类
    public static readonly ConcurrentQueue<byte[]> ActiveAudioSet = new ConcurrentQueue<byte[]>(); //实时语音识别队列
    public static readonly ConcurrentQueue<string> AudioFileQueue = new ConcurrentQueue<string>(); //文件识别队列
    private bool isRunning = true; //控制语音识别是否工作的标志
    private static TaskManager taskManager;
    public SpeechToTextViewModel(IMapper mapper,
        IContainerProvider container,
        Services.LanguageService languageService,
        Services.DialogService dialogService,
         IEventAggregator eventAggregator)
        : base(mapper, container, languageService, dialogService, eventAggregator)
    {

    }

    /// <summary>
    /// 初始化视图模型.
    /// </summary>
    [RelayCommand]
    private void Initialize()
    {
        //视频采集实例化
        waveCollect = new WaveCollectService();
        //WebSocket连接状态监测
        websocketClient = new WebSocketService(ServerIp, ServerPort);
        //初始化语音识别消息接收
        websocketClient.OnMessageReceived += WebsocketClient_OnMessageReceived;

    }

    /// <summary>
    /// 显示返回消息
    /// </summary>
    /// <param name="message"></param>
    private void WebsocketClient_OnMessageReceived(string message)
    {
        if (!string.IsNullOrEmpty(message))
        {
            try
            {
                //将接收到服务器发来的消息进行json转换
                JsonDocument jsonDoc = JsonDocument.Parse(message);
                JsonElement root = jsonDoc.RootElement;
                JsonElement modeElement; JsonElement textElement; JsonElement isFinalElement;
                //判读root中有没有mode、text和is_final
                if (root.TryGetProperty("mode", out modeElement) &&
                    root.TryGetProperty("text", out textElement) &&
                    root.TryGetProperty("is_final", out isFinalElement))
                {
                    string? mode = modeElement.GetString();
                    string? text = textElement.GetString();
                    bool isfinal = isFinalElement.GetBoolean();

                    if (mode == "2pass-online" && websocketClient.OnlineASRMode != "offline")
                    {
                        websocketClient.Onlinebuff += text;
                        AsrMessageResult = websocketClient.Recbuff + websocketClient.Onlinebuff;
                        //OnMessageReceived?.Invoke(recbuff + onlinebuff);//触发语音识别内容接收事件
                    }
                    else if (mode == "2pass-offline" || mode == "offline")
                    {
                        websocketClient.Recbuff += text;
                        websocketClient.Onlinebuff = string.Empty;
                        AsrMessageResult = websocketClient.Recbuff;
                        //OnMessageReceived?.Invoke(recbuff);
                    }
                    else if (mode == "online")
                    {
                        websocketClient.Onlinebuff += text;
                        AsrMessageResult = websocketClient.Recbuff + websocketClient.Onlinebuff;
                        //OnMessageReceived?.Invoke(recbuff + onlinebuff);
                    }
                    if (isfinal && websocketClient.OnlineASRMode != "offline")
                    {
                        websocketClient.Recbuff = string.Empty;
                    }

                    //文件转换结束则停止转换
                    if (RecoderModeType == RecoderModeType.File && isfinal)
                    {
                        Task.Run(async () =>await Stop());
                    }
                }
            }
            catch (JsonException ex)
            {
                MessageManager.Error("JSON 解析错误: " + ex.Message + "\n收到的消息内容为:" + message);
            }
        }

    }

    /// <summary>
    /// 选择要转文字的语音文件
    /// </summary>
    [RelayCommand]
    private Task SelectWavFile()
    {
        OpenFileDialog openFileDialog = new OpenFileDialog();
        openFileDialog.Filter = "音频文件(*.wav,*.pcm,*.mp3,*.mp4)|*.wav;*.pcm;*.mp3;*.mp4"; // 可以设置过滤，比如只选择图片： "Image files (*.jpg, *.png)|*.jpg;*.png"
        if (openFileDialog.ShowDialog() == true)
        {
            //清空接收内容缓存，方便进行下一次语音识别内容展示
            websocketClient.Recbuff = string.Empty;
            websocketClient.Onlinebuff = string.Empty;
            websocketClient.OnlineASRMode = "offline";
            WavFilePath = openFileDialog.FileName;
        }
        return Task.CompletedTask;
    }

    /// <summary>
    /// 开始语音识别
    /// </summary>
    [RelayCommand]
    private Task Start()
    {
        //清空接收内容缓存，方便进行下一次语音识别内容展示
        websocketClient.Recbuff = string.Empty;
        websocketClient.Onlinebuff = string.Empty;
        isRunning = true; //开始语音识别循环

        //初始化多线程任务管理器
        taskManager = new TaskManager();
        taskManager.Start(async ct => { await StartASR(); }, TaskPriority.High, "StartASR");
        taskManager.Start(async ct => { await SendAudioToSeverAsync(); }, TaskPriority.High, "SendAudioToSeverAsync");
        taskManager.Start(async ct => { await SendAudioFileToSeverAsync(); }, TaskPriority.High, "SendAudioFileToSeverAsync");
        ////启动语音转文字服务
        //Thread asr = new Thread(StartASR);
        //asr.Start();

        ////启动在线语音转录服务
        //Thread SendAudioThread = new Thread(SendAudioToSeverAsync);
        //SendAudioThread.Start();

        ////启动音频文件转录服务
        //Thread AudioFileThread = new Thread(SendAudioFileToSeverAsync);
        //AudioFileThread.Start();

        if (RecoderModeType == RecoderModeType.File && !string.IsNullOrEmpty(WavFilePath))
        {
            AudioFileQueue.Enqueue(WavFilePath);//将文件路径添加到文件识别队列
        }

        IsEnableStart = false;
        IsEnableStop = true;
        return Task.CompletedTask;
    }

    [RelayCommand]
    private async Task Stop()
    {
        try
        {
            waveCollect.StopRec(); // 停止录音
            websocketClient.ClientLastConnOnline(); // 停止发送语音信息
            await taskManager.StopAllAsync(TimeSpan.FromSeconds(5)); //停止

            isRunning = false; // 停止循环    
            IsEnableStart = true;
            IsEnableStop = false;
            WavFilePath = string.Empty;

            MessageManager.Success("停止语音转换！");
        }
        catch (Exception ex)
        {
            MessageManager.Error(ex.Message);
        }


    }

    [RelayCommand]
    private async Task Connect()
    {
        if (websocketClient.IsRunning)
        {
            MessageManager.Warning("语音服务已连接！");
            return;
        }
        var result = await websocketClient.ClientConnect();
        if (result)
        {
            IsEnableConnect = false;
            IsEnableDisConnect = true;
            IsEnableStart = true;
            IsEnableStop = false;
            MessageManager.Success("语音服务连接成功！");
        }
        else
        {
            IsEnableConnect = true;
            IsEnableDisConnect = false;
            IsEnableStart = false;
            IsEnableStop = false;
            MessageManager.Error("语音服务连接失败！");
        }

    }

    [RelayCommand]
    private async Task DisConnect()
    {
        if (!websocketClient.IsRunning)
        {
            MessageManager.Warning("语音服务已断开！");
            return;
        }
        var result = await websocketClient.ClientDisConnect();
        if (result)
        {
            IsEnableConnect = true;
            IsEnableDisConnect = false;
            IsEnableStart = false;
            IsEnableStop = false;
            MessageManager.Success("语音服务连接成功！");
        }
        else
        {
            MessageManager.Error("语音服务断开失败！");
        }

    }

    partial void OnRecoderModeTypeChanged(RecoderModeType value)
    {
        if (value is RecoderModeType.File)
        {
            IsEnableWavFile = true;
        }
        else
        {
            IsEnableWavFile = false;
            WavFilePath = string.Empty;
        }
    }

    #region 后台方法
    /// <summary>
    /// 在线语音识别服务函数
    /// </summary>
    /// <param name="str"></param>
    private Task StartASR()
    {
        string onlineasrmode = "online";
        switch (this.ASRModeType)
        {
            case ASRModeType.TwoPass:
                onlineasrmode = "2pass";
                break;
            case ASRModeType.OnLine:
                onlineasrmode = "online";
                break;
            case ASRModeType.OffLine:
                onlineasrmode = "offline";
                break;
            default:
                break;
        }

        //开始录制声音、发送识别
        waveCollect.StartRec();//开始进行音频采集
        websocketClient.ClientFirstConnOnline(onlineasrmode);//初始发送数据，包括模式、热词、
        try
        {
            while (isRunning)
            {
                if (!waveCollect.voicebuff.IsEmpty)
                {
                    byte[] buff;
                    int buffcnt = waveCollect.voicebuff.Count;
                    waveCollect.voicebuff.TryDequeue(out buff);
                    if (buff != null)
                        ActiveAudioSet.Enqueue(buff);
                }
                Thread.Sleep(10);
            }
        }
        catch
        {
            MessageManager.Error("实时识别出现异常！");
        }
        finally
        {
            waveCollect.StopRec();
            websocketClient.ClientLastConnOnline();
        }

        return Task.CompletedTask;
    }

    /// <summary>
    /// 将实时语音识别队列发送给服务器
    /// </summary>
    private Task SendAudioToSeverAsync()
    {
        while (true)
        {
            if (ActiveAudioSet.Count > 0)
            {
                byte[] audio;
                ActiveAudioSet.TryDequeue(out audio);
                if (audio == null)
                {
                    continue;
                }
                byte[] mArray = new byte[audio.Length];
                Array.Copy(audio, 0, mArray, 0, audio.Length);
                if (mArray != null)
                {
                    websocketClient.ClientSendAudioFunc(mArray);
                }
            }
            else
            {
                Thread.Sleep(10);
            }
        }

    }

    /// <summary>
    /// 将文件识别队列发送给服务器
    /// </summary>
    private Task SendAudioFileToSeverAsync()
    {
        while (true)
        {
            Thread.Sleep(1000);
            if (AudioFileQueue.Count > 0)
            {
                string filepath = string.Empty;
                AudioFileQueue.TryDequeue(out filepath);
                if (filepath != string.Empty && filepath != null)
                {
                    websocketClient.ClientSendFileFunc(filepath);
                }
            }
            else
            {
                Thread.Sleep(100);
            }
        }
    }
    #endregion
}
